﻿#if USING_TIMELINE
using Devil;
using GameToolkit.UI;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

namespace GameToolkit
{

    sealed public class PanelClip : PlayableAsset
    {
        [System.Serializable]
        public abstract class IntentProvider
        {
            internal virtual void Open(PanelBehaviour user, PanelTrack.PanelReference panel)
            {
                panel.Open(user);
            }

        }

        [System.Serializable]
        public abstract class IntentProvider<T> : IntentProvider
        {
            public abstract T GetIntent(PanelBehaviour user);
            internal override void Open(PanelBehaviour user, PanelTrack.PanelReference panel)
            {
                panel.Open(user, GetIntent(user));
            }
        }

        [SerializeField]
        bool m_PauseTimeline;

        [SerializeField, AssetPath(typeof(GameObject))]
        AssetAddress m_UsingPanel;

        [SerializeReference]
        IntentProvider m_Intent;

        PanelBehaviour mTemplate;
        public string UsingPanel => m_UsingPanel.ToString();

        public string Title
        {
            get
            {
                if (m_UsingPanel.IsNull)
                    return "unssigned";
                else
                    return NativeFileSystem.GetFileName(m_UsingPanel, true);
            }
        }

#if UNITY_EDITOR
        private void OnValidate()
        {
            name = Title;
        }
#endif

        public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
        {
            if (mTemplate == null)
            {
                mTemplate = new PanelBehaviour(this);
            }
            var play = ScriptPlayable<PanelBehaviour>.Create(graph, mTemplate);
            return play;
        }

        sealed public class PanelBehaviour : PlayableBehaviour
        {
            PanelClip mClipAsset;
            string mUI;
            PanelTrack.PanelTrackBehaviour mTrackBehaviour;
            PanelTrack.PanelReference mRef;
            PlaybackTrack.PlaybackController mPlaybackCtrl;
            internal List<SignalAsset> mSignals;
            public GameObject Owner => mTrackBehaviour == null ? null : mTrackBehaviour.mParent;

            public PanelBehaviour() { }

            public PanelBehaviour(PanelClip clip)
            {
                mClipAsset = clip;
            }

            public override void OnPlayableCreate(Playable playable)
            {
                mUI = mClipAsset == null ? null : mClipAsset.m_UsingPanel.ToString();
                if (PanelManager.Instance != null)
                    mUI = PanelManager.Instance.GetRealUri(mUI);
            }

            public override void OnGraphStart(Playable playable)
            {
                if (mClipAsset.m_PauseTimeline)
                    mPlaybackCtrl = PlaybackTrack.GetPlaybackController(playable);
                var output = playable.GetOutput(0);
                if(output.GetPlayableType() == typeof(PanelTrack.PanelTrackBehaviour))
                {
                    mTrackBehaviour = ((ScriptPlayable<PanelTrack.PanelTrackBehaviour>)output).GetBehaviour();
                    mRef = mTrackBehaviour == null ? null : mTrackBehaviour.GetReference(mUI);
                }
            }

            public void EmitSignal(SignalAsset signal)
            {
                if (mSignals == null)
                    mSignals = new List<SignalAsset>();
                mSignals.Add(signal);
            }

            public void OnPanelClosed()
            {
                if(mPlaybackCtrl != null)
                {
                    mPlaybackCtrl.Resume(this);
                }
            }

            public override void ProcessFrame(Playable playable, FrameData info, object playerData)
            {
                if (mSignals == null || mSignals.Count == 0)
                    return;
                var receiver = playerData as SignalReceiver;
                if (receiver == null)
                    return;
                for (int i = 0; i < mSignals.Count; i++)
                {
                    var sig = mSignals[i];
                    if (sig == null)
                        continue;
                    var act = receiver.GetReaction(sig);
                    if (act != null)
                        act.Invoke();
                }
                mSignals.Clear();
            }

            public override void OnBehaviourPlay(Playable playable, FrameData info)
            {
                if (mRef != null)
                {
                    if (mClipAsset.m_Intent != null)
                        mClipAsset.m_Intent.Open(this, mRef);
                    else
                        mRef.Open(this);
                    if(mPlaybackCtrl != null)
                    {
                        mPlaybackCtrl.Pause(this);
                    }
                }
            }

            public override void OnBehaviourPause(Playable playable, FrameData info)
            {
                if(mPlaybackCtrl != null)
                {
                    mPlaybackCtrl.Resume(this);
                }
                if(mRef != null)
                {
                    mRef.Unuse(this);
                }
            }
        }
    }
}

#endif